/*
  +----------------------------------------------------------------------+
  | PHP Version 5                                                        |
  +----------------------------------------------------------------------+
  | Copyright (c) 1997-2013 The PHP Group                                |
  +----------------------------------------------------------------------+
  | This source file is subject to version 3.01 of the PHP license,      |
  | that is bundled with this package in the file LICENSE, and is        |
  | available through the world-wide-web at the following url:           |
  | http://www.php.net/license/3_01.txt                                  |
  | If you did not receive a copy of the PHP license and are unable to   |
  | obtain it through the world-wide-web, please send a note to          |
  | license@php.net so we can mail you a copy immediately.               |
  +----------------------------------------------------------------------+
  | Author:                                                              |
  +----------------------------------------------------------------------+
*/ 

/* $Id$ */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include "php.h"
#include "php_ini.h"
#include "ext/standard/info.h"
#include "ext/standard/php_var.h"  
#include "ext/standard/php_string.h"  
#include "ext/standard/php_smart_str.h"  
#include "ext/standard/php_filestat.h"
#include "main/SAPI.h"
#include "main/php_streams.h"
#include "Zend/zend_API.h"
#include "Zend/zend_alloc.h"
#include "Zend/zend_interfaces.h"
#include "Zend/zend_compile.h"
#include "ext/pcre/php_pcre.h"
#include "php_wk_framework.h"
#include "wk_view.h"
#include <time.h>

static int le_template;

zend_class_entry *ext_view_ce, *ext_view_impl_ce;

ZEND_BEGIN_ARG_INFO_EX(void_arginfo, 0, 0, 0)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(view_set_dir_arginfo, 0, 0, 1)
	ZEND_ARG_INFO(0, dir)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(view_set_ext_arginfo, 0, 0, 1)
	ZEND_ARG_INFO(0, time)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(view_set_open_cache_arginfo, 0, 0, 1)
	ZEND_ARG_INFO(0, open)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(view_template_arginfo, 0, 0, 1)
	ZEND_ARG_INFO(0, content)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(view_assign_arginfo, 0, 0, 2)
	ZEND_ARG_INFO(0, key)
	ZEND_ARG_INFO(0, value)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(view_assigns_arginfo, 0, 0, 1)
	ZEND_ARG_INFO(0, vals)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(view_display_arginfo, 0, 0, 1)
	ZEND_ARG_INFO(0, template)
	ZEND_ARG_INFO(0, compile_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(view_fetch_arginfo, 0, 0, 1)
	ZEND_ARG_INFO(0, template)
	ZEND_ARG_INFO(0, compile_id)
ZEND_END_ARG_INFO()

ZEND_BEGIN_ARG_INFO_EX(view_iscache_arginfo, 0, 0, 1)
	ZEND_ARG_INFO(0, template)
	ZEND_ARG_INFO(0, cache_id)
	ZEND_ARG_INFO(0, compile_id)
ZEND_END_ARG_INFO()

static zend_function_entry ext_view_impl_methods[] = {
	/*ext_view_impl::setTemplateDir*/
	ZEND_ABSTRACT_ME(ext_view_impl, setTemplateDir, view_set_dir_arginfo)
	/*ext_view_impl::getTemplateDir*/
	ZEND_ABSTRACT_ME(ext_view_impl, getTemplateDir, void_arginfo)
	/*ext_view_impl::setCompileDir*/
	ZEND_ABSTRACT_ME(ext_view_impl, setCompileDir, view_set_dir_arginfo)
	/*ext_view_impl::getCompileDir*/
	ZEND_ABSTRACT_ME(ext_view_impl, getCompileDir, void_arginfo)
	/*ext_view_impl::setFileExt*/
	ZEND_ABSTRACT_ME(ext_view_impl, setFileExt, view_set_ext_arginfo)
	/*ext_view_impl::getFileExt*/
	ZEND_ABSTRACT_ME(ext_view_impl, getFileExt, void_arginfo)
	/*ext_view_impl::setOpenCache*/
	ZEND_ABSTRACT_ME(ext_view_impl, setOpenCache, view_set_open_cache_arginfo)
	/*ext_view_impl::getOpenCache*/
	ZEND_ABSTRACT_ME(ext_view_impl, getOpenCache, void_arginfo)
	/*ext_view_impl::template*/
	ZEND_ABSTRACT_ME(ext_view_impl, template, view_template_arginfo)
	/*ext_view_impl::assign*/
	ZEND_ABSTRACT_ME(ext_view_impl, assign, view_assign_arginfo)
	/*ext_view_impl::assigns*/
	ZEND_ABSTRACT_ME(ext_view_impl, assigns, view_assigns_arginfo)
	/*ext_view_impl::display*/
	ZEND_ABSTRACT_ME(ext_view_impl, display, view_display_arginfo)
	/*ext_view_impl::fetch*/
	ZEND_ABSTRACT_ME(ext_view_impl, fetch, view_fetch_arginfo)
	{NULL,NULL,NULL}
};

#define PARSER_NUMS 20

//检测文件夹是否存在,不存在则尝试创建
static int check_folder_exists(char *fullpath){
	php_stream_statbuf ssb;
	
	if (FAILURE == php_stream_stat_path(fullpath, &ssb)) {
		if (!php_stream_mkdir(fullpath, 0755,  PHP_STREAM_MKDIR_RECURSIVE, NULL)) {
			zend_error(E_ERROR, "could not create directory \"%s\"", fullpath);
			return FAILURE;
		}
	}
	return SUCCESS;
}

static int parser_templates(php_stream *stream, char *compile_path){
	zval *replace_val;
	int   result_len, i;
	char *result;

	/**
	 * i：如果在修饰符中加上"i"，则正则将会取消大小写敏感性，即"a"和"A" 是一样的。 
	 * s：如果在修饰符中加入"s"，那么默认的"."代表除了换行符以外的任何字符将会变成任意字符，也就是包括换行符！
	 * e：本修饰符仅仅对于replacement有用，代表在replacement中作为PHP代码。 
	 */
	char regex[PARSER_NUMS][100] = {
		"/([\\n\\r]+)\\t+/s",
		"/\\<\\!\\-\\-\\{(.+?)\\}\\-\\-\\>/s",
		"/\\{template\\s+(\\S+)\\}/",
		"/\\{{if\\s+(.+?)\\}}/",
		"/\\{{else\\}}/",
		"/\\{{\\/if\\}}/",
		"/\\{if\\s+(.+?)\\}/is",
		"/\\{elseif\\s+(.+?)\\}/is",
		"/\\{else\\}/i",
		"/\\{\\/if\\}/i",
		"/\\``if\\s+(.+?)\\``/",
		"/\\``else``/",
		"/\\``\\/if\\``/",
		"/\\{loop\\s+(\\S+)\\s+(\\S+)\\}/s",
		"/\\{loop\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\}/s",
		"/\\{\\/loop\\}/i",
		"/\\{(\\$[a-zA-Z_\\x7f-\\xff][a-zA-Z0-9_\\x7f-\\xff]*)\\}/", //替换{$var} => <?php echo $var;?>/\{(\\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)\}/
		"/\\{(\\$[a-zA-Z0-9_\\[\\]'\"\$\\x7f-\\xff]+)\\}/s", //替换{$var} => <?php echo $var;?>/\{(\\$[a-zA-Z0-9_\[\]\'\"\$\x7f-\xff]+)\}/s
		"/\\{([A-Z_\\x7f-\\xff][A-Z0-9_\x7f-\xff]*)\\}/s",    //替换{var} => <?php echo var;?>
		"/[\\n\\r\\t]*\{eval\\s+(.+?)\\s*\\}[\\n\\r\\t]*/is",
	};
	
	char replace[PARSER_NUMS][100] = {
		"\\1",
		"{\\1}",
		"<?php ext_view::template('\\1')?>",
		"``if \\1``",
		"``else``",
		"``/if``",
		"<?php if(\\1) { ?>",
		"<?php } elseif(\\1) { ?>",
		"<?php } else { ?>",
		"<?php } ?>",
		"{{if \\1}}",
		"{{else}}",
		"{{/if}}",
		"<?php if(is_array(\\1)) { foreach(\\1 as \\2) { ?>",
		"<?php if(is_array(\\1)) { foreach(\\1 as \\2 => \\3) { ?>",
		"<?php } } ?>",
		"<?php echo htmlspecialchars(\\1, ENT_COMPAT);?>",
		"<?php echo htmlspecialchars(\\1, ENT_COMPAT);?>",
		"<?php echo \\1;?>",
		"<?php \\1?>",
	};

	char subject[1024];
	smart_str content = {0};

	while(!php_stream_eof(stream)) {
		if(!php_stream_gets(stream, subject, 1024)){
			break;
		}
		smart_str_appendl(&content, subject, strlen(subject));
	}

	smart_str_0(&content);

	MAKE_STD_ZVAL(replace_val);
	content.c = estrndup(content.c, content.len);

	for(i=0; i<PARSER_NUMS; i++){
		ZVAL_STRINGL(replace_val, replace[i], strlen(replace[i]), 1);
		if((result = php_pcre_replace(regex[i], strlen(regex[i]),
								  content.c, content.len,
								  replace_val, 0,
								  &result_len, -1, NULL TSRMLS_CC)) != NULL){
			
			content.c = result;
			content.len = result_len;
		}else {
			efree(content.c);
		}
		
	}
	php_stream_close(stream);

	stream = php_stream_open_wrapper(compile_path, "wb", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL);

	php_stream_write_string(stream, result);

	if(stream == NULL){
		zend_error(E_WARNING, "%s does not read able", compile_path);
		return 1;
	}

	php_stream_close(stream);

	if(result != NULL) efree(result);

	return 0;
}

static void include_template(zend_file_handle *file_handle, zval *assigns){
	zend_op_array *op_array;
	HashTable *calling_symbol_table;
	op_array = zend_compile_file(file_handle, ZEND_REQUIRE TSRMLS_CC);

	if (EG(active_symbol_table)) {
		calling_symbol_table = EG(active_symbol_table);
	} else {
		calling_symbol_table = NULL;
	}

	ALLOC_HASHTABLE(EG(active_symbol_table));
	zend_hash_init(EG(active_symbol_table), 0, NULL, ZVAL_PTR_DTOR, 0);

	if(Z_TYPE_P(assigns) == IS_ARRAY){
		for(zend_hash_internal_pointer_reset(Z_ARRVAL_P(assigns)); 
			zend_hash_has_more_elements(Z_ARRVAL_P(assigns)) == SUCCESS;
			zend_hash_move_forward(Z_ARRVAL_P(assigns))
			){
			char *key;
			int key_len;
			ulong idx;
			zval **data;

			if(zend_hash_get_current_key_ex(Z_ARRVAL_P(assigns), &key, &key_len, &idx, 0, NULL) != HASH_KEY_IS_STRING){
				continue;
			}

			if(zend_hash_get_current_data(Z_ARRVAL_P(assigns), (void**)&data) == FAILURE){
				continue;
			}

			ZEND_SET_SYMBOL_WITH_LENGTH(EG(active_symbol_table), key, key_len,
						*data, Z_REFCOUNT_P(*data) + 1, PZVAL_IS_REF(*data));
		}	
	}

	if (op_array && file_handle->handle.stream.handle) {
		int dummy = 1;
		zend_hash_add(&EG(included_files), file_handle->opened_path, strlen(file_handle->opened_path)+1, (void *)&dummy, sizeof(int), NULL);
	}
	zend_destroy_file_handle(file_handle TSRMLS_CC);

	if (op_array) {
		zval *result					= NULL;
		STORE_EG_ENVIRON();

		EG(return_value_ptr_ptr)	= &result;
		EG(active_op_array) 		= op_array;

#if ((PHP_MAJOR_VERSION == 5) && (PHP_MINOR_VERSION > 2)) || (PHP_MAJOR_VERSION > 5)
		if (!EG(active_symbol_table)) {
			zend_rebuild_symbol_table(TSRMLS_C);
		}
#endif
		zend_execute(op_array TSRMLS_CC);

		destroy_op_array(op_array TSRMLS_CC);
		efree(op_array);

		if (!EG(exception)) {
			if (EG(return_value_ptr_ptr)) {
				zval_ptr_dtor(EG(return_value_ptr_ptr));
			}
		}

		RESTORE_EG_ENVIRON();
	}

	if (calling_symbol_table) {
		FREE_HASHTABLE(EG(active_symbol_table));
		EG(active_symbol_table) = calling_symbol_table;
	}
}

static void display(INTERNAL_FUNCTION_PARAMETERS, int is_obstart){
	zval *tpl;
	zval *is_cache, *template_dir, file_exists, *compile_dir, compile_file_exists;
	php_stream *stream;
	smart_str file_path = {0};
	smart_str compile_path = {0};
	zend_file_handle file_handle;
	zval *assigns, *file_ext;
	size_t tpath_len,cpath_len;
	char *tpath, *cpath;

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &tpl) == FAILURE){
		RETURN_FALSE;
	}

	assigns 	 = zend_read_property(FETCH_THIS, ZEND_STRL("assign"), 0 TSRMLS_CC);
	is_cache 	 = zend_read_property(FETCH_THIS, ZEND_STRL("is_cache"), 0 TSRMLS_CC);
	template_dir = zend_read_property(FETCH_THIS, ZEND_STRL("template_dir"), 0 TSRMLS_CC);
	compile_dir  = zend_read_property(FETCH_THIS, ZEND_STRL("compile_dir"), 0 TSRMLS_CC);
	file_ext   	 = zend_read_property(FETCH_THIS, ZEND_STRL("file_ext"), 0 TSRMLS_CC);

	//编译后的模板文件路径
	smart_str_appendl(&compile_path, Z_STRVAL_P(compile_dir), Z_STRLEN_P(compile_dir));
	smart_str_appendl(&compile_path, Z_STRVAL_P(tpl), Z_STRLEN_P(tpl));
	smart_str_appendl(&compile_path, ".php", 4);
	smart_str_0(&compile_path);

	cpath = estrndup(compile_path.c, compile_path.len);
	cpath_len = php_dirname(cpath, compile_path.len);

	if(check_folder_exists(cpath) == FAILURE){
		RETURN_FALSE;
	}

	//如果is_cache开启，则从编译缓存读取文件
	if(Z_LVAL_P(is_cache) > 0){

		php_stat(compile_path.c, compile_path.len, FS_EXISTS, &compile_file_exists TSRMLS_CC);
		if(Z_LVAL(compile_file_exists) == 1){
			goto display;
		}
		
	}

	//模板文件路径
	smart_str_appendl(&file_path, Z_STRVAL_P(template_dir), Z_STRLEN_P(template_dir));
	smart_str_appendl(&file_path, Z_STRVAL_P(tpl), Z_STRLEN_P(tpl));
	smart_str_appendl(&file_path, Z_STRVAL_P(file_ext), Z_STRLEN_P(file_ext));
	smart_str_0(&file_path);

	tpath = estrndup(file_path.c, file_path.len);
	tpath_len = php_dirname(tpath, file_path.len);

	if(check_folder_exists(tpath) == FAILURE){
		RETURN_FALSE;
	}
	php_stat(file_path.c, file_path.len, FS_EXISTS, &file_exists TSRMLS_CC);
	if(Z_LVAL(file_exists) == 0){

		zend_error(E_WARNING, "file:%s not exists!", file_path.c);
		RETURN_FALSE;

	}

	stream = php_stream_open_wrapper(file_path.c, "rb", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL);
	if(stream == NULL){

		zend_error(E_WARNING, "%s does not read able", file_path.c);
		RETURN_FALSE;

	}else{
		parser_templates(stream, compile_path.c);
		goto display;

	}
display:
	if(zend_stream_open(compile_path.c, &file_handle TSRMLS_CC) == SUCCESS){
		if(is_obstart){
			#if ZEND_MODULE_API_NO >= 20121212
				if(php_output_start_default(TSRMLS_CC) == SUCCESS){
					include_template(&file_handle, assigns);
					
					php_output_get_contents(return_value TSRMLS_CC);
					php_output_end(TSRMLS_CC);

					RETURN_ZVAL(return_value, 0 , NULL);
				}else{
					RETURN_FALSE;
				}
			#else
				if(php_start_ob_buffer(NULL, 0, 0 TSRMLS_CC) == SUCCESS){
					include_template(&file_handle, assigns);
				
					php_ob_get_buffer(return_value TSRMLS_CC);
					php_end_ob_buffer(0, 0 TSRMLS_CC);

					RETURN_ZVAL(return_value, 0 , NULL);
				}else{
					RETURN_FALSE;
				}
			#endif

		}else{
			include_template(&file_handle, assigns);
		}
	}else{
		zend_error(E_WARNING, "%s does not open", file_path.c);
		RETURN_FALSE;
	}
}

/**
 * ext_view::setTemplateDir
 */
ZEND_METHOD(ext_view, setTemplateDir){
	zval *template_dir;

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &template_dir) == FAILURE){
		RETURN_FALSE;
	}

	if(Z_TYPE_P(template_dir) == IS_STRING){
		zend_update_property(FETCH_THIS, ZEND_STRL("template_dir"), template_dir TSRMLS_CC);
		RETURN_TRUE;
	}else{
		RETURN_FALSE;
	}
}

ZEND_METHOD(ext_view, getTemplateDir){
	zval *template_dir;
	template_dir = zend_read_property(FETCH_THIS, ZEND_STRL("template_dir"), 0 TSRMLS_CC);

	if(Z_TYPE_P(template_dir) != IS_NULL){
		RETURN_STRINGL(Z_STRVAL_P(template_dir), Z_STRLEN_P(template_dir), 1);
	}

	RETURN_FALSE;
}

ZEND_METHOD(ext_view, setCompileDir){
	zval *compile_dir;

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &compile_dir) == FAILURE){
		RETURN_FALSE;
	}

	if(Z_TYPE_P(compile_dir) == IS_STRING){
		zend_update_property(FETCH_THIS, ZEND_STRL("compile_dir"), compile_dir TSRMLS_CC);
		RETURN_TRUE;
	}else{
		RETURN_FALSE;
	}
}

ZEND_METHOD(ext_view, getCompileDir){
	zval *compile_dir;
	compile_dir = zend_read_property(FETCH_THIS, ZEND_STRL("compile_dir"), 0 TSRMLS_CC);

	if(Z_TYPE_P(compile_dir) != IS_NULL){
		RETURN_STRINGL(Z_STRVAL_P(compile_dir), Z_STRLEN_P(compile_dir), 1);
	}

	RETURN_FALSE;
}

ZEND_METHOD(ext_view, setFileExt){
	zval *ext;

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &ext) == FAILURE){
		RETURN_FALSE;
	}

	if(Z_TYPE_P(ext) == IS_STRING){
		zend_update_property_stringl(FETCH_THIS, ZEND_STRL("file_ext"), Z_STRVAL_P(ext), Z_STRLEN_P(ext) TSRMLS_CC);
		RETURN_TRUE;
	}else{
		RETURN_FALSE;
	}
}

ZEND_METHOD(ext_view, getFileExt){
	zval *ext;
	ext = zend_read_property(FETCH_THIS, ZEND_STRL("file_ext"), 0 TSRMLS_CC);

	RETURN_ZVAL(ext, 0, NULL);
}

ZEND_METHOD(ext_view, setOpenCache){
	zval *is_cache;

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &is_cache) == FAILURE){
		RETURN_FALSE;
	}

	convert_to_long(is_cache);

	zend_update_property(FETCH_THIS, ZEND_STRL("is_cache"), is_cache TSRMLS_CC);

	RETURN_TRUE;
}

ZEND_METHOD(ext_view, getOpenCache){
	zval *is_cache;
	is_cache = zend_read_property(FETCH_THIS, ZEND_STRL("is_cache"), 0 TSRMLS_CC);

	if(Z_TYPE_P(is_cache) != IS_NULL){
		convert_to_long(is_cache);
		RETURN_LONG(Z_LVAL_P(is_cache));
	}

	RETURN_LONG(0);
}

ZEND_METHOD(ext_view, template){
	zval *tpl, *file_ext;
	zval *is_cache, *template_dir, file_exists, *compile_dir, compile_file_exists;
	php_stream *stream;
	char file_path[255], cache_path[255], compile_path[255];
	zend_file_handle file_handle;
	zval *assigns;
	size_t tpath_len,cpath_len;
	char *tpath, *cpath;

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &tpl) == FAILURE){
		RETURN_FALSE;
	}

	assigns 	 = zend_read_property(FETCH_THIS, ZEND_STRL("assign"), 0 TSRMLS_CC);
	is_cache 	 = zend_read_property(FETCH_THIS, ZEND_STRL("is_cache"), 0 TSRMLS_CC);
	template_dir = zend_read_property(FETCH_THIS, ZEND_STRL("template_dir"), 0 TSRMLS_CC);
	compile_dir  = zend_read_property(FETCH_THIS, ZEND_STRL("compile_dir"), 0 TSRMLS_CC);
	file_ext   	 = zend_read_property(FETCH_THIS, ZEND_STRL("file_ext"), 0 TSRMLS_CC);

	//模板文件路径
	sprintf(file_path, "%s%s%s", Z_STRVAL_P(template_dir), Z_STRVAL_P(tpl), Z_STRVAL_P(file_ext));
	tpath = estrndup(file_path, strlen(file_path));
	tpath_len = php_dirname(tpath, strlen(file_path));

	if(check_folder_exists(tpath) == FAILURE){
		RETURN_FALSE;
	}
	//编译后的模板文件路径
	sprintf(compile_path, "%s%s.php", Z_STRVAL_P(compile_dir), Z_STRVAL_P(tpl));
	cpath = estrndup(compile_path, strlen(compile_path));
	cpath_len = php_dirname(cpath, strlen(compile_path));

	if(check_folder_exists(cpath) == FAILURE){
		RETURN_FALSE;
	}


	//如果is_cache开启，则从编译缓存读取文件
	if(Z_LVAL_P(is_cache) > 0){

		php_stat(compile_path, strlen(compile_path), FS_EXISTS, &compile_file_exists TSRMLS_CC);
		//若编译缓存已经存在，则直接读取
		if(Z_LVAL(compile_file_exists) == 1) goto display;
		
	}

	php_stat(file_path, strlen(file_path), FS_EXISTS, &file_exists TSRMLS_CC);

	if(Z_LVAL(file_exists) == 0){

		zend_error(E_WARNING, "file:%s not exists!", file_path);
		RETURN_FALSE;

	}

	stream = php_stream_open_wrapper(file_path, "rb", REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL);

	if(stream == NULL){

		zend_error(E_WARNING, "%s does not read able", file_path);
		RETURN_FALSE;

	}else{
		parser_templates(stream, compile_path);
		goto display;

	}

display:
	if(zend_stream_open(compile_path, &file_handle TSRMLS_CC) == SUCCESS){

		include_template(&file_handle, assigns);
		RETURN_TRUE;

	}else{

		zend_error(E_WARNING, "%s does not open", file_path);
		RETURN_FALSE;
		
	}
}

ZEND_METHOD(ext_view, assign){
	zval *key, *value, *assign;

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "zz", &key, &value) == FAILURE){
		RETURN_FALSE;
	}

	assign = zend_read_property(FETCH_THIS, ZEND_STRL("assign"), 0 TSRMLS_CC);

	if(Z_TYPE_P(assign) == IS_NULL){
		MAKE_STD_ZVAL(assign);
		array_init(assign);
	}

	convert_to_string(key);
	zval_addref_p(value);
	add_assoc_zval(assign, Z_STRVAL_P(key), value);

	zend_update_property(FETCH_THIS, ZEND_STRL("assign"), assign TSRMLS_CC);
	RETURN_TRUE;
}

ZEND_METHOD(ext_view, assigns){
	zval *vals, *assign;

	if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "z", &vals) == FAILURE){
		RETURN_FALSE;
	}

	if(Z_TYPE_P(vals) != IS_ARRAY){
		zend_error(E_ERROR, "assgins muts be array");
		RETURN_FALSE;
	}

	zval_addref_p(vals);
	assign = zend_read_property(FETCH_THIS, ZEND_STRL("assign"), 0 TSRMLS_CC);

	if(Z_TYPE_P(assign) == IS_NULL){
		MAKE_STD_ZVAL(assign);
		array_init(assign);
	}

	for(zend_hash_internal_pointer_reset(Z_ARRVAL_P(vals));
		zend_hash_has_more_elements(Z_ARRVAL_P(vals)) == SUCCESS;
		zend_hash_move_forward(Z_ARRVAL_P(vals))){
			char *key;
			int key_len;
			ulong idx;
			zval **data;

			if(zend_hash_get_current_key_ex(Z_ARRVAL_P(vals), &key, &key_len, &idx, 0, NULL) != HASH_KEY_IS_STRING){
				continue;
			}

			if(zend_hash_get_current_data(Z_ARRVAL_P(vals), (void**)&data) == FAILURE){
				continue;
			}

			add_assoc_zval(assign, key, *data);
	}

	zend_update_property(FETCH_THIS, ZEND_STRL("assign"), assign TSRMLS_CC);

	RETURN_TRUE;
}

ZEND_METHOD(ext_view, display){
	display(INTERNAL_FUNCTION_PARAM_PASSTHRU, 0);
}

ZEND_METHOD(ext_view, fetch){
	display(INTERNAL_FUNCTION_PARAM_PASSTHRU, 1);
}

static zend_function_entry ext_view_methods[] = {
	ZEND_ME(ext_view, setTemplateDir, view_set_dir_arginfo, ZEND_ACC_PUBLIC)
	ZEND_ME(ext_view, getTemplateDir, void_arginfo,			ZEND_ACC_PUBLIC)
	ZEND_ME(ext_view, setCompileDir, view_set_dir_arginfo,	ZEND_ACC_PUBLIC)
	ZEND_ME(ext_view, getCompileDir, void_arginfo,			ZEND_ACC_PUBLIC)
	ZEND_ME(ext_view, setFileExt, view_set_ext_arginfo,		ZEND_ACC_PUBLIC)
	ZEND_ME(ext_view, getFileExt, void_arginfo,				ZEND_ACC_PUBLIC)
	ZEND_ME(ext_view, setOpenCache, view_set_open_cache_arginfo,ZEND_ACC_PUBLIC)
	ZEND_ME(ext_view, getOpenCache, void_arginfo,			ZEND_ACC_PUBLIC)
	ZEND_ME(ext_view, template, view_template_arginfo,ZEND_ACC_PUBLIC)
	ZEND_ME(ext_view, assign, view_assign_arginfo,			ZEND_ACC_PUBLIC)
	ZEND_ME(ext_view, assigns, view_assigns_arginfo,		ZEND_ACC_PUBLIC)
	ZEND_ME(ext_view, display, view_display_arginfo,		ZEND_ACC_PUBLIC)
	ZEND_ME(ext_view, fetch, view_fetch_arginfo,			ZEND_ACC_PUBLIC)
	{NULL,NULL,NULL}
};

/* {{{ PHP_MINIT_FUNCTION
 */
EXT_STARTUP_FUNCTION(template)
{
	/* If you have INI entries, uncomment these lines 
	REGISTER_INI_ENTRIES();
	*/
	zend_class_entry view_impl_ce, view_ce;
	INIT_CLASS_ENTRY(view_impl_ce, "ext_view_impl", ext_view_impl_methods);
	ext_view_impl_ce = zend_register_internal_interface(&view_impl_ce TSRMLS_CC);

	INIT_CLASS_ENTRY(view_ce	 , "ext_view"	  , ext_view_methods);
	ext_view_ce 	 = zend_register_internal_class(&view_ce TSRMLS_CC);
	zend_class_implements(ext_view_ce TSRMLS_CC, 1, ext_view_impl_ce);

	zend_declare_property_string(ext_view_ce, ZEND_STRL("template_dir"), "", ZEND_ACC_PUBLIC TSRMLS_CC);
	zend_declare_property_string(ext_view_ce, ZEND_STRL("complie_dir"), "", ZEND_ACC_PUBLIC TSRMLS_CC);
	zend_declare_property_string(ext_view_ce, ZEND_STRL("file_ext"), ".htm", ZEND_ACC_PUBLIC TSRMLS_CC);
	zend_declare_property_long(ext_view_ce, ZEND_STRL("is_cache"), 0, ZEND_ACC_PUBLIC TSRMLS_CC);
	zend_declare_property_null(ext_view_ce, ZEND_STRL("assign") , ZEND_ACC_PUBLIC TSRMLS_CC);
	return SUCCESS;
}
/* }}} */